using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


namespace MetaWorld
{
	/// <summary>
	/// Specialized SceneGraphNode for displaying textured triangle lists.
	/// </summary>
	abstract public class TexturedTriangleList : SceneGraphNode
	{
		/// <summary>
		/// Internal: number of triangles. Since this is a triangle list, 
		/// number of vertices is (TriangleCount * 3).
		/// </summary>
		protected int TriangleCount;

		/// <summary>
		/// Internal: bitmap holding unprocessed texture
		/// </summary>
		protected Bitmap TextureBitmap;

		/// <summary>
		/// Internal: vertex buffer
		/// </summary>
		protected VertexBuffer VertexBuffer;

		/// <summary>
		/// Internal: texture for the triangle list
		/// </summary>
		protected Texture Texture;


		/// <summary>
		/// Constructor. Texture is allowed to be null.
		/// </summary>
		/// <param name="triangleCount"></param>
		/// <param name="texture"></param>
		public TexturedTriangleList (int triangleCount, Bitmap texture) : base () 
		{
			TriangleCount = triangleCount;
			TextureBitmap = texture;
		}

		/// <summary>
		/// Constructor for untextured triangle list
		/// </summary>
		/// <param name="triangleCount"></param>
		public TexturedTriangleList (int triangleCount) : this (triangleCount, null) { }

		/// <summary>
		/// Sets the texture to the given bitmap, and the diffuse color to white.
		/// </summary>
		/// <param name="texture"></param>
		public void SetTextureAndColor (Bitmap texture)
		{
			this.Texture = Texture.FromBitmap (Scene.Device, texture, 0, Pool.Managed);
			this.ColorDiffuse = new ColorWrapper (Color.White);
		}			

		/// <summary>
		/// Initialize the triangle list. Note: calls the FillVertexBuffer function
		/// defined by children.
		/// </summary>
		/// <param name="scene"></param>
		public override void Begin (Scene scene)
		{
			base.Begin (scene);
			this.VertexBuffer = new VertexBuffer (typeof (CustomVertex.PositionNormalTextured),
				TriangleCount * 3,
				Scene.Device, 
				Usage.Dynamic | Usage.WriteOnly,
				CustomVertex.PositionNormalTextured.Format,
				Pool.Default);
			if (TextureBitmap != null) SetTextureAndColor (TextureBitmap);
			FillVertexBuffer ();
		}

		/// <summary>
		/// Populates the vertex buffer with data. Called by Begin.
		/// </summary>
		protected virtual void FillVertexBuffer () { }

		/// <summary>
		/// Internal: converts a DirectX Vector3 structure into an array of three floats
		/// </summary>
		/// <param name="vector"></param>
		/// <returns></returns>
		protected float[] MakeFloatArray (Vector3 vector)
		{
			float[] result = new float [3];
			result[0] = vector.X;
			result[1] = vector.Y;
			result[2] = vector.Z;
			return result;
		}

		/// <summary>
		/// Renders the triangle list within the given scene
		/// </summary>
		public override void RenderSelf () 
		{

			if (this.usingPixelShader)
			{
				// We're using a pixel shader for textures - gotta be careful
				// about letting the shader in on the settings

				// Export the following information to the shader:
				// matrix multiplication of world * view * projection transforms
				Matrix WorldViewProj = 
					Scene.Device.Transform.World *
					Scene.Device.Transform.View *
					Scene.Device.Transform.Projection;
				this.pixelShaderEffect.SetValue ("WorldViewProj", WorldViewProj);

				// DON'T DO THIS JUST YET...
				// lighting information 
				// Light l0 = Scene.Device.Lights[0];
				// this.PixelShaderEffect.SetValue ("LightEnabled0", l0.Enabled);
				// this.PixelShaderEffect.SetValue ("LightType0", Convert.ToInt32(l0.Type));
				// this.PixelShaderEffect.SetValue ("LightDiffuse0", l0.DiffuseColor);
				// this.PixelShaderEffect.SetValue ("LightAmbient0", l0.AmbientColor);
				// this.PixelShaderEffect.SetValue ("LightPosition0", MakeFloatArray (l0.Position));
				// this.PixelShaderEffect.SetValue ("LightDirection0", MakeFloatArray (l0.Direction));
				

				// Set the entry point and let's roll!
				this.pixelShaderEffect.Technique = this.pixelShaderTechniqueName;

				int Passes = this.pixelShaderEffect.Begin (0);
				for (int i = 0; i < Passes; i++) 
				{
					this.pixelShaderEffect.BeginPass (i);
					Scene.Device.SetTexture (0, Texture);
					Scene.Device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
					Scene.Device.SetStreamSource (0, VertexBuffer, 0);
					Scene.Device.DrawPrimitives (PrimitiveType.TriangleList, 0, TriangleCount);
					this.pixelShaderEffect.EndPass ();
				}
				this.pixelShaderEffect.End ();
			}
			else
			{
				// We're not using a pixel shader for textures; do it the old way

				Scene.Device.SetTexture (0, Texture);
				Scene.Device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
				Scene.Device.SetStreamSource (0, VertexBuffer, 0);
				Scene.Device.DrawPrimitives (PrimitiveType.TriangleList, 0, TriangleCount);
			}
		}
	}

	
	/// <summary>
	/// Generic triangle class.
	/// </summary>
	public class TexturedTriangle : TexturedTriangleList 
	{

		/// <summary>
		/// Internal: storage for triangle vertex data,
		/// texture data, and normal.
		/// </summary>
		protected float x1, y1, z1, u1, v1, 
			x2, y2, z2, u2, v2, 
			x3, y3, z3, u3, v3,
			xn, yn, zn;

		/// <summary>
		/// Constructor. Takes position coordinates (x, y, z) and texture coordinates (u, v)
		/// for each vertex of the triangle, as well as normal coordinates (xn, yn, zn) 
		/// for the entire triangle.
		/// </summary>
		/// <param name="texture"></param>
		/// <param name="x1"></param>
		/// <param name="y1"></param>
		/// <param name="z1"></param>
		/// <param name="u1"></param>
		/// <param name="v1"></param>
		/// <param name="x2"></param>
		/// <param name="y2"></param>
		/// <param name="z2"></param>
		/// <param name="u2"></param>
		/// <param name="v2"></param>
		/// <param name="x3"></param>
		/// <param name="y3"></param>
		/// <param name="z3"></param>
		/// <param name="u3"></param>
		/// <param name="v3"></param>
		/// <param name="xn"></param>
		/// <param name="yn"></param>
		/// <param name="zn"></param>
		public TexturedTriangle (Bitmap texture,
			float x1, float y1, float z1, float u1, float v1,
			float x2, float y2, float z2, float u2, float v2,
			float x3, float y3, float z3, float u3, float v3,
			float xn, float yn, float zn) 
			: base (1, texture)
		{
			this.x1 = x1; this.y1 = y1; this.z1 = z1; this.u1 = u1; this.v1 = v1;
			this.x2 = x2; this.y2 = y2; this.z2 = z2; this.u2 = u2; this.v2 = v2;
			this.x3 = x3; this.y3 = y3; this.z3 = z3; this.u3 = u3; this.v1 = v3;
			this.xn = xn; this.yn = yn; this.zn = zn;
		}

		/// <summary>
		/// Initialize triangle list
		/// </summary>
		protected override void FillVertexBuffer()
		{
			base.FillVertexBuffer ();
			CustomVertex.PositionNormalTextured [] verts = new CustomVertex.PositionNormalTextured [3];
			verts[0] = new CustomVertex.PositionNormalTextured (x1, y1, z1, xn, yn, zn, u1, v1);
			verts[1] = new CustomVertex.PositionNormalTextured (x2, y2, z2, xn, yn, zn, u2, v2);
			verts[2] = new CustomVertex.PositionNormalTextured (x3, y3, z3, xn, yn, zn, u3, v3);
			VertexBuffer.SetData (verts, 0, LockFlags.None);
		}
	}

	
	/// <summary>
	/// Equilateral triangle, with each side of unit length, presented as
	/// laying flat on the x/y plane, facing the -z direction. Texture file
	/// maps directly onto the triangle.
	/// </summary>
	public class TexturedEquilateralTriangle : TexturedTriangle
	{
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="texture"></param>
		public TexturedEquilateralTriangle (Bitmap texture)
			: base (texture,
			-0.5f, 0f, 0f, 1f, 0f,
			0f, 0.866f, 0f, 0f, 0.5f,
			0.5f, 0f, 0f, 1f, 1f,
			0f, 0f, -1f)
		{ }

	}

	
	/// <summary>
	/// Unit square, laying flat on the x/y plane, facing in the -z direction,
	/// centered at the origin. Texture maps directly onto the square.
	/// </summary>
	public class TexturedSquare : TexturedTriangleList 
	{
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="texture"></param>
		public TexturedSquare (Bitmap texture) : base (2, texture) { }

		/// <summary>
		/// Initialize triangle list
		/// </summary>
		protected override void FillVertexBuffer()
		{
			base.FillVertexBuffer ();
			CustomVertex.PositionNormalTextured [] verts = new CustomVertex.PositionNormalTextured [6];

			verts[0] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f, 0f, 0f, 0f, -1f, 1f, 0f);
			verts[1] = new CustomVertex.PositionNormalTextured (-0.5f, 0.5f,  0f, 0f, 0f, -1f, 0f, 0f);
			verts[2] = new CustomVertex.PositionNormalTextured (0.5f, 0.5f,   0f, 0f, 0f, -1f, 0f, 1f);

			verts[3] = new CustomVertex.PositionNormalTextured (0.5f, 0.5f,   0f, 0f, 0f, -1f, 0f, 1f);
			verts[4] = new CustomVertex.PositionNormalTextured (0.5f, -0.5f,  0f, 0f, 0f, -1f, 1f, 1f);
			verts[5] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f, 0f, 0f, 0f, -1f, 1f, 0f);
			
			VertexBuffer.SetData (verts, 0, LockFlags.None);
		}
	}

		
	/// <summary>
	/// Unit cube, made of unit squares, pointing outwards.
	/// </summary>
	public class TexturedBox : TexturedTriangleList 
	{
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="texture"></param>
		public TexturedBox (Bitmap texture) : base (12, texture) { }

		/// <summary>
		/// Initialize vertex buffer
		/// </summary>
		protected override void FillVertexBuffer()
		{
			base.FillVertexBuffer ();
			CustomVertex.PositionNormalTextured [] verts = 
				new CustomVertex.PositionNormalTextured [36]; 

			// front face
			verts[0] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f, -0.5f, 0f, 0f, -1f, 1f, 0f);
			verts[1] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f, -0.5f, 0f, 0f, -1f, 0f, 0f);
			verts[2] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f, -0.5f, 0f, 0f, -1f, 0f, 1f);

			verts[3] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f, -0.5f, 0f, 0f, -1f, 0f, 1f);
			verts[4] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f, -0.5f, 0f, 0f, -1f, 1f, 1f);
			verts[5] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f, -0.5f, 0f, 0f, -1f, 1f, 0f);

			// back face
			verts[6]  = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f,  0.5f, 0f, 0f,  1f, 1f, 0f);
			verts[7]  = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f,  0.5f, 0f, 0f,  1f, 0f, 0f);
			verts[8]  = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f,  0.5f, 0f, 0f,  1f, 0f, 1f);

			verts[9]  = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f,  0.5f, 0f, 0f,  1f, 0f, 1f);
			verts[10] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f,  0.5f, 0f, 0f,  1f, 1f, 1f);
			verts[11] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f,  0.5f, 0f, 0f,  1f, 1f, 0f);

			// left face
			verts[12] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f,  0.5f, -1f, 0f, 0f, 1f, 0f);
			verts[13] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f,  0.5f, -1f, 0f, 0f, 0f, 0f);
			verts[14] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f, -0.5f, -1f, 0f, 0f, 0f, 1f);
 
			verts[15] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f, -0.5f, -1f, 0f, 0f, 0f, 1f);
			verts[16] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f, -0.5f, -1f, 0f, 0f, 1f, 1f);
			verts[17] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f,  0.5f, -1f, 0f, 0f, 1f, 0f);

			// right face
			verts[18] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f, -0.5f,  1f, 0f, 0f, 1f, 0f);
			verts[19] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f, -0.5f,  1f, 0f, 0f, 0f, 0f);
			verts[20] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f,  0.5f,  1f, 0f, 0f, 0f, 1f);
 
			verts[21] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f,  0.5f,  1f, 0f, 0f, 0f, 1f);
			verts[22] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f,  0.5f,  1f, 0f, 0f, 1f, 1f);
			verts[23] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f, -0.5f,  1f, 0f, 0f, 1f, 0f);

			// top face
			verts[24] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f, -0.5f, 0f,  1f, 0f, 1f, 0f);
			verts[25] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f,  0.5f, 0f,  1f, 0f, 0f, 0f);
			verts[26] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f,  0.5f, 0f,  1f, 0f, 0f, 1f);
 
			verts[27] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f,  0.5f, 0f,  1f, 0f, 0f, 1f);
			verts[28] = new CustomVertex.PositionNormalTextured ( 0.5f,  0.5f, -0.5f, 0f,  1f, 0f, 1f, 1f);
			verts[29] = new CustomVertex.PositionNormalTextured (-0.5f,  0.5f, -0.5f, 0f,  1f, 0f, 1f, 0f);

			// bottom face
			verts[30] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f,  0.5f, 0f, -1f, 0f, 1f, 0f);
			verts[31] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f, -0.5f, 0f, -1f, 0f, 0f, 0f);
			verts[32] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f, -0.5f, 0f, -1f, 0f, 0f, 1f);
 
			verts[33] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f, -0.5f, 0f, -1f, 0f, 0f, 1f);
			verts[34] = new CustomVertex.PositionNormalTextured ( 0.5f, -0.5f,  0.5f, 0f, -1f, 0f, 1f, 1f);
			verts[35] = new CustomVertex.PositionNormalTextured (-0.5f, -0.5f,  0.5f, 0f, -1f, 0f, 1f, 0f);

			VertexBuffer.SetData (verts, 0, LockFlags.None);
		}
	}


}